package org.xx.armory.doc;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static org.xx.armory.commons.Validators.rejectIfBlank;
import static org.xx.armory.commons.Validators.rejectIfNull;

/**
 * 文档元素的属性集合。
 *
 * @author Haart
 */
public final class ElementProperties
        implements Map<String, Object> {
    public static final String FONT_FAMILY = "font-family";
    public static final String FONT_SIZE = "font-size";
    public static final String FONT_WEIGHT = "font-weight";

    public static final String COLOR = "color";
    public static final String BACKGROUND_COLOR = "background-color";

    public static final String TEXT_ALIGN = "text-align";
    public static final String TEXT_INDENT = "text-indent";
    public static final String TEXT_DECORATION = "text-decoration";

    public static final String WIDTH = "width";
    public static final String HEIGHT = "height";

    public static final String MARGIN_LEFT = "margin-left";
    public static final String MARGIN_RIGHT = "margin-right";
    public static final String MARGIN_TOP = "margin-top";
    public static final String MARGIN_BOTTOM = "margin-bottom";

    public static final String ALIGN = "align";

    private final Map<String, Object> props;

    /**
     * 构造文档元素的属性集合。
     */
    public ElementProperties() {
        this.props = new HashMap<>();
    }

    /**
     * 根据文档元素的属性集合构造新的集合。
     *
     * @param other
     *         原有的文档元素属性集合。
     */
    public ElementProperties(ElementProperties other) {
        this.props = (other != null && other.props != null) ? new HashMap<>(other.props) : new HashMap<>();
    }

    /**
     * 合并属性集合。
     *
     * @param array
     *         待合并的属性集合。
     * @return 合并后的属性集合, 如果参数{@code array}是{@code null}或者不包含任何元素则返回空的元素集合。
     */
    public static ElementProperties merge(ElementProperties... array) {
        final ElementProperties ret = new ElementProperties();

        if (array == null) {
            return ret;
        }

        for (final ElementProperties p : array) {
            if (p == null) {
                continue;
            }

            ret.props.putAll(p);
        }

        return ret;
    }

    /**
     * 扩展属性集合。
     *
     * @param src
     *         源属性集合。
     * @param array
     *         待合并的属性集合。
     * @return 合并后的属性集合, 如果参数{@code src}不是{@code null}那么先合并{@code src}的内容, 如果参数{@code array}是
     * {@code null}或者不包含任何元素则不继续合并，否则按照传入参数的顺序逐个合并。
     */
    public static ElementProperties extend(
            ElementProperties src,
            ElementProperties... array
    ) {
        final ElementProperties ret = new ElementProperties();

        if (src != null) {
            ret.props.putAll(src);
        }

        if (array == null) {
            return ret;
        }

        for (final ElementProperties p : array) {
            if (p == null) {
                continue;
            }

            ret.props.putAll(p);
        }

        return ret;
    }

    /**
     * 确认键名存在, 如果此键名不存在则创建新的键。
     *
     * @param key
     *         键名。
     * @return 正规化并且转为小写的键名。
     * @throws NullPointerException
     *         如果参数{@code key}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code key}只包含空白字符。
     */
    private static String ensureKey(String key) {
        return rejectIfBlank(key, "key").trim().toLowerCase();
    }

    /**
     * 按照键名从属性集合中查询整数属性。
     *
     * @param key
     *         键名。
     * @return 对应的整数属性值, 如果找不到该属性则返回{@code 0}。
     */
    public final int getInteger(String key) {
        final Object o = this.props.get(key);
        if (o instanceof String) {
            return Integer.parseInt((String) o);
        } else if (o instanceof Number) {
            return ((Number) o).intValue();
        } else {
            return 0;
        }
    }

    /**
     * 按照键名从属性集合中查询长整数属性。
     *
     * @param key
     *         键名。
     * @return 对应的长整数属性值, 如果找不到该属性则返回{@code 0}。
     */
    public final long getLong(
            String key
    ) {
        final Object o = this.props.get(key);
        if (o instanceof String) {
            return Long.parseLong((String) o);
        } else if (o instanceof Number) {
            return ((Number) o).longValue();
        } else {
            return 0;
        }
    }

    /**
     * 按照键名从属性集合中查询尺寸属性。
     *
     * @param key
     *         键名。
     * @return 对应的尺寸属性值, 如果找不到该属性则返回{@code null}。
     */
    public final ElementSize getSize(
            String key
    ) {
        final Object o = this.props.get(key);
        if (o instanceof ElementSize) {
            return (ElementSize) o;
        } else {
            return new ElementSize(0, ElementSize.ElementSizeType.AUTO);
        }
    }

    /**
     * 按照键名从属性集合中查询浮点数属性。
     *
     * @param key
     *         键名。
     * @return 对应的浮点数属性值, 如果找不到该属性则返回{@code 0}。
     */
    public final float getFloat(
            String key
    ) {
        final Object o = this.props.get(key);
        if (o instanceof String) {
            return Float.parseFloat((String) o);
        } else if (o instanceof Number) {
            return ((Number) o).floatValue();
        } else {
            return 0;
        }
    }

    /**
     * 按照键名从属性集合中查询双精度属性。
     *
     * @param key
     *         键名。
     * @return 对应的双精度属性值, 如果找不到该属性则返回{@code 0}。
     */
    public final double getDouble(String key) {
        final Object o = this.props.get(key);
        if (o instanceof String) {
            return Double.parseDouble((String) o);
        } else if (o instanceof Number) {
            return ((Number) o).doubleValue();
        } else {
            return 0;
        }
    }

    /**
     * 按照键名从属性集合中查询字符串属性。
     *
     * @param key
     *         键名。
     * @return 对应的字符串属性值, 如果找不到该属性则返回空白字符串({@code ""})。
     */
    public final String getString(String key) {
        final Object o = this.props.get(key);

        return o == null ? "" : o.toString().trim();
    }

    @Override
    public int size() {
        return this.props.size();
    }

    @Override
    public boolean isEmpty() {
        return this.props.isEmpty();
    }

    @Override
    public boolean containsKey(
            Object key
    ) {
        rejectIfNull(key, "key");

        return this.props.containsKey(ensureKey(key.toString()));
    }

    @Override
    public boolean containsValue(Object value) {
        return this.props.containsValue(value);
    }

    @Override
    public Object get(Object key) {
        rejectIfNull(key, "key");

        return this.props.get(ensureKey(key.toString()));
    }

    @Override
    public Object put(
            String key,
            Object value
    ) {
        return this.props.put(ensureKey(key), value);
    }

    @Override
    public Object remove(Object key) {
        rejectIfNull(key, "key");

        return this.props.remove(ensureKey(key.toString()));
    }

    @Override
    public void putAll(Map<? extends String, ?> m) {
        this.props.putAll(m);
    }

    @Override
    public void clear() {
        this.props.clear();
    }

    @Override
    public Set<String> keySet() {
        return this.props.keySet();
    }

    @Override
    public Collection<Object> values() {
        return this.props.values();
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        return this.props.entrySet();
    }

    /**
     * 向属性集合中添加新属性，如果指定的键已存在则覆盖。
     *
     * @param key
     *         属性的键。
     * @param value
     *         属性的值。
     * @return 属性集合本身。
     */
    public final ElementProperties add(
            String key,
            Object value
    ) {
        put(key, value);
        return this;
    }

    @Override
    public final String toString() {
        final StringBuilder sb = new StringBuilder();
        for (final Entry<String, Object> entry : this.props.entrySet()) {
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append(";");
        }
        return sb.toString();
    }
}
